﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Devmil.MVVM.Notification;
using System.Linq.Expressions;
using System.Reflection;

namespace Devmil.MVVM.Dependencies
{
    public class NotifyDependencyTracker : IDisposable
    {
        public static NotifyDependencyTracker Track<T, R>(T propertyObject, Expression<Func<T, R>> propertyExpression)
        {
            return new NotifyDependencyTracker(propertyObject, NotifyPropertyChangedHandler.GetMemberName(propertyExpression));
        }

        private static Dictionary<int, Stack<NotifyDependencyNode>> nodesPerThread = new Dictionary<int, Stack<NotifyDependencyNode>>();
        
        private int level;
        public NotifyDependencyTracker(object propertyObject, string propertyName)
        {
            lock (nodesPerThread)
                if (!nodesPerThread.ContainsKey(CurrentManagedThreadId))
                    nodesPerThread.Add(CurrentManagedThreadId, new Stack<NotifyDependencyNode>());
            level = nodesPerThread[CurrentManagedThreadId].Count;

            object sender = null;
            var dependencyStack = nodesPerThread[CurrentManagedThreadId];
            if (level > 0
                && dependencyStack.Peek().GetterTargetObject.IsAlive)
                sender = dependencyStack.Peek().GetterTargetObject.Target;

            dependencyStack.Push(new NotifyDependencyNode(sender, propertyObject, propertyName));
        }

        public void Dispose()
        {
            if (level == 0)
                lock (nodesPerThread)
                    nodesPerThread.Remove(CurrentManagedThreadId);
            else
            {
                NotifyDependencyCache.AddPath(new NotifyDependencyPath(nodesPerThread[CurrentManagedThreadId]));
                nodesPerThread[CurrentManagedThreadId].Pop();
            }
        }

        public int CurrentManagedThreadId 
        {
            get
            {
#if WP7
                return System.Threading.Thread.CurrentThread.ManagedThreadId;
#else
                return Environment.CurrentManagedThreadId;
#endif
            }
        }
    }
}
